package net.ib.mtalk.ui.attachment.video;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

import net.ib.mtalk.constant.AttachmentConstant;
import net.ib.mtalk.util.MTalkLogWriter;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnInfoListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * MediaRecorder를 간단하게 사용하기 위한 래핑. 동영상 녹화를 한다.
 * 이상한 동작을 요구하면 아무 것도 하지 않음.
 * @author Arngard
 *
 */
public class VideoRecordManager {

	private String targetPath;
	private MediaRecorder recorder = new MediaRecorder();

	/**
	 * State of state machine for this recorder. 상태 플래그.
	 * 각 상태에 따라 버튼의 동작이 결정되어야 한다.
	 * @author Arngard
	 */
	public static enum State {
		/**	아직 리소스가 준비되지 않았다. */
		BEFORE_START,
		/** 리소스가 준비되어, 이제 즉시 녹화를 시작할 수 있다. */
		READY_TO_START,
		/** 녹화가 시작되었다. */
		RECORDING,
		/** 녹화가 종료되었고 결과 처리를 하고 있다. 추가 작업은 이루어지지 않을 것이다. */
		TERMINATED
	}
	/** 현재 상태를 나타냄 */
	private State currentState = State.BEFORE_START;
	/**
	 * @return 현재의 상태
	 */
	public State getState() {
		return currentState;
	}

	/**
	 * {@link #startRecorder()}를 할 수 있도록 준비한다.
	 * @param path 다음 녹화를 할 때 기록할 파일의 경로.
	 * @param surfaceHolder 녹화중인 화면을 보여줄 {@link SurfaceView}의 holder. null이면 보여주지 않으면서 녹화하도록 준비한다.
	 * @throws IOException 리소스 초기화에 실패했다. 경로에 문제가 있지 않은지?
	 */
	synchronized public void resetRecorder(String path, SurfaceHolder surfaceHolder, OnInfoListener listener) throws Exception {
		if (currentState == State.BEFORE_START) {
			try {
				// reset the recorder
				recorder.reset();
			} catch(Exception e) {
				MTalkLogWriter.e(e);
			}
		} else if (currentState == State.READY_TO_START) {
			return;
		} else if (currentState == State.RECORDING) {
			try {
				recorder.reset();//recorder.stop();
				new File(targetPath).delete();	// targetPath에 저장되었을 녹음 파일을 삭제한다.
			} catch(Exception e) {
				MTalkLogWriter.e(e);
			}
		} else if (currentState == State.TERMINATED) {
			return;
		}

		try {
			// configure the recorder
			configRecorderLight(path, surfaceHolder, listener);

			targetPath = path;

			// now it is prepared
			recorder.prepare();

			currentState = State.READY_TO_START;
			MTalkLogWriter.i(this.getClass().getSimpleName()+".resetRecorder(): success");
		} catch (IllegalStateException e) {
			MTalkLogWriter.e(e);
		}
	}

	/**
	 * 녹화가 시작된다.
	 * {@link #resetRecorder(String)} 혹은 {@link #stopRecorder()}을 할 수 있다.
	 */
	synchronized public void startRecorder() {

		switch(currentState) {
		case BEFORE_START:
		case RECORDING:
		case TERMINATED:
			return;
			//녹화 가능 시작 할 때에만 레코딩 시작
		case READY_TO_START:
			try {
				recorder.start();

				currentState = State.RECORDING;
				MTalkLogWriter.i(this.getClass().getSimpleName()+".startRecorder(): success");
			} catch (IllegalStateException e) {
				MTalkLogWriter.e(e);
			}
		}
	}

	/**
	 * 녹화가 끝났다. 결과 처리를 한다.
	 * @return 녹화가 저장된 파일 경로.
	 */
	synchronized public String stopRecorder() {
		if (currentState == State.BEFORE_START) {
			currentState = State.TERMINATED;
			return null;
		} else if (currentState == State.READY_TO_START) {
			MTalkLogWriter.i(this.getClass().getSimpleName()+".stopRecorder(): goto cancelRecorder()");
			cancelRecorder();
			return null;
		} else if (currentState == State.RECORDING) {
			// continue
		} else if (currentState == State.TERMINATED) {
			return null;
		}

		try {
			recorder.reset();//recorder.stop();
			recorder.release();

			currentState = State.TERMINATED;
			MTalkLogWriter.i(this.getClass().getSimpleName()+".stopRecorder(): success");
			return targetPath;
		} catch (IllegalStateException e) {
			MTalkLogWriter.e(e);
		}

		return null;
	}

	/**
	 * 녹화를 하지 않고 그냥 나가려 한다. 파일 찌꺼기를 지우고 자원을 반납한다. 
	 */
	synchronized public void cancelRecorder() {
		if (currentState == State.BEFORE_START) {
			currentState = State.TERMINATED;
			return;
		} else if (currentState == State.READY_TO_START) {
			// continue
		} else if (currentState == State.RECORDING) {
			// continue
		} else if (currentState == State.TERMINATED) {
			return;
		}

		try {
			recorder.reset();
			recorder.release();

			new File(targetPath).delete();	// targetPath에 저장되었을 녹음 파일을 삭제한다.

			currentState = State.TERMINATED;
			MTalkLogWriter.i(this.getClass().getSimpleName()+".cancelRecorder(): success");
		} catch(Exception e) {
			MTalkLogWriter.e(e);
		}
	}

	@Override
	protected void finalize() throws Throwable {
		// 반드시 장치 리소스를 반납해야 한다.
		cancelRecorder();

		super.finalize();
	}

	private void configRecorderLight(String path, SurfaceHolder surfaceHolder, OnInfoListener listener) {
		if (listener != null) {
			recorder.setOnInfoListener(listener);
		}
		if (surfaceHolder != null) {
			recorder.setPreviewDisplay(surfaceHolder.getSurface());
		}

		try {
			recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
		} catch(Exception e) {
			recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
		}
		try {
			recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
		} catch(Exception e) {
			recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
		}

		if(isQualityRecording) {
			try {	// API 레벨 호환을 위해 클래스를 동적으로 로딩하는 구간
				// dynamic loading
				Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("android.media.CamcorderProfile");
				Method methodGet = clazz.getMethod("get", new Class[] {int.class});
				Method methodSetProfile = recorder.getClass().getMethod("setProfile", new Class[] {clazz});

				// ready to run
				Object[] args = new Object[1];

				// run: CamcorderProfile camcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
				args[0] = methodGet.invoke(clazz, new Object[] {1});

				// run: recorder.setProfile(camcorderProfile);
				methodSetProfile.invoke(recorder, args);
			} catch(Exception e) {
				MTalkLogWriter.e(e);
			}
		} else {
			try {
				recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
			} catch(Exception e) {
				recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
			}
			try {
				recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
			} catch(Exception e) {
				recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
			}
			try {
				recorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
			} catch(Exception e) {
				recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
			}
			//recorder.setVideoFrameRate(15);
			//recorder.setVideoSize(176, 144);
		}

		recorder.setOutputFile(path);
		recorder.setMaxDuration(AttachmentConstant.RECORD_MAX_DURATION_MILLIS);
	}

	boolean isQualityRecording = false;

	public void setQualityRecording(boolean isChecked) {
		currentState = State.BEFORE_START;
		isQualityRecording = isChecked;
	}
}
